Skip to content

MDEV-37482: Contention on btr_sea::partition::latch; introduce innodb_adaptive_hash_index_cells - #4264

Merged
dr-m merged 5 commits into
11.8from
MDEV-37482
Sep 30, 2025
Merged

MDEV-37482: Contention on btr_sea::partition::latch; introduce innodb_adaptive_hash_index_cells#4264
dr-m merged 5 commits into
11.8from
MDEV-37482

Conversation

@dr-m

@dr-m dr-m commented Aug 27, 2025

Copy link
Copy Markdown
Contributor
  • The Jira issue number for this PR is: MDEV-37482

Description

The number of hash table cells in the InnoDB adaptive hash index was fixed on the initial innodb_buffer_pool_size and insufficient for some workloads, leading to excessively long hash bucket chains.

Furthermore, btr_sea::partition::insert() and btr_sea::partition::erase() operations will be optimized to prefer a combination of a shared latch and a page_hash_lock that is pushed down to the hash table. In this way, these operations can run concurrently with each other as well as searches on other parts of the hash table of the same btr_sea::partition.

Release Notes

We introduce the parameter innodb_adaptive_hash_index_cells that can be configured with SET GLOBAL. The specified value will be effectively multiplied by innodb_adaptive_hash_index_parts, because each partition will contain its own hash table.

How can this PR be tested?

Basing the PR against the correct MariaDB version

  • This is a new feature or a refactoring, and the PR is based against the main branch.
  • This is a bug fix, and the PR is based against the earliest maintained branch in which the bug can be reproduced.

PR quality check

  • I checked the CODING_STANDARDS.md file and my PR conforms to this where appropriate.
  • For any trivial modifications to the PR, I am ok with the reviewer making the changes themselves.

@dr-m
dr-m requested a review from vlad-lesin August 27, 2025 11:49
@dr-m dr-m self-assigned this Aug 27, 2025
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Comment on lines +3769 to +3773
static MYSQL_SYSVAR_UINT(adaptive_hash_index_cells, btr_search.n_cells,
PLUGIN_VAR_RQCMDARG,
"Number of adaptive hash table cells",
nullptr, innodb_adaptive_hash_index_cells_update, 0, 0, UINT_MAX, 0);
#endif /* BTR_CUR_HASH_ADAPT */

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iMineLink pointed out that this forms an artificial limitation on really large buffer pools. This would have to be size_t instead of uint.

Furthermore, I’d like to see if we could repurpose innodb_adaptive_hash_index_parts. That is, hard-wire the partitions to 1, and make that parameter control the hash table size. Possibly, it could be feasible to replace the btr_sea::partition::latch with latches that are embedded in the hash array, like we do it for buf_pool.page_hash and lock_sys.rec_hash.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the number of partitions becomes hardwired to 1, I believe that having a size_t variable for the number of cells may be less interesting as the CRC32C does not provide enough bits of entropy to distribute the values over more than 232-1 cells, making uint wide enough. If wider hash would be implemented (xxHash could be an interesting candidate), it would make more sense to switch to size_t.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I did not realize that the uint actually corresponds to the available entropy of CRC-32C. Anything larger than that definitely is useless.

We actually have an implementation of xxHash in the rocksdb submodule. For some reason, its AVX-512 implementation failed to compile in my environment, and I had commented it out:

diff --git a/util/xxhash.h b/util/xxhash.h
index 9846861b7..d38c83ef5 100644
--- a/util/xxhash.h
+++ b/util/xxhash.h
@@ -2745,7 +2745,7 @@ enum XXH_VECTOR_TYPE /* fake enum */ {
 #endif
 
 #ifndef XXH_VECTOR    /* can be defined on command line */
-#  if defined(__AVX512F__)
+#  if 0&&defined(__AVX512F__)
 #    define XXH_VECTOR XXH_AVX512
 #  elif defined(__AVX2__)
 #    define XXH_VECTOR XXH_AVX2

It would be interesting to test the relative performance of this, compared to CRC-32C, on a few different processor implementations (one with AVX512 for both, and another comparing the AVX2 implementation to crc32c_3way()). While doing this, we must keep in mind that dtuple_fold() is currently computing a hash piecewise, one field at a time. It looks like XXH64_update() in the above code would support appending any number of bytes to the hash. It looks like we may have more comprehensive cross-ISA SIMD coverage for our crc32 implementations than for xxHash.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following would avoid the compilation error without disabling the AVX512 implementation:

diff --git a/util/xxhash.h b/util/xxhash.h
index 9846861b7..a5742a157 100644
--- a/util/xxhash.h
+++ b/util/xxhash.h
@@ -3658,7 +3658,7 @@ XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
     }
 }
 
-XXH_FORCE_INLINE XXH_TARGET_AVX512 void
+XXH_TARGET_AVX512 void
 XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
 {
     XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0);

This patch is apparently only needed with CMAKE_BUILD_TYPE=Debug, not with CMAKE_BUILD_TYPE=RelWithDebInfo.
The upstream source code https://github.com/Cyan4973/xxHash is more up to date and includes a benchmark:

make -C tests/bench clean benchHash_avx512
tests/bench/benchHash_avx512

Compared to the plain benchHash, the benchHash_avx512 appears to speed up xxh3 and XXH128 only. I understood that there is a 64-bit and a 128-bit variant of XXH3, which the benchmark is referring to with the names xxh3 and XXH128.

The interface to the XXH32 function seems to be compatible with crc32 functions. I added the crc32_avx512.cc from https://github.com/dr-m/crc32_simd to the directory and patched the code to make use of it:

diff --git a/tests/bench/Makefile b/tests/bench/Makefile
index ec5d56f..974a2f7 100644
--- a/tests/bench/Makefile
+++ b/tests/bench/Makefile
@@ -36,7 +36,7 @@ CXXFLAGS ?= -O3
 LDFLAGS  += $(MOREFLAGS)
 
 
-OBJ_LIST  = main.o bhDisplay.o benchHash.o benchfn.o timefn.o
+OBJ_LIST  = main.o bhDisplay.o benchHash.o benchfn.o timefn.o crc32_avx512.o
 
 
 default: benchHash
diff --git a/tests/bench/hashes.h b/tests/bench/hashes.h
index 39f6e0f..d4f3c8f 100644
--- a/tests/bench/hashes.h
+++ b/tests/bench/hashes.h
@@ -66,10 +66,12 @@
 #define XXH_INLINE_ALL
 #include "xxhash.h"
 
+unsigned crc32c_refl_vpclmulqdq(unsigned crc, const void *buf, size_t size);
+
 size_t XXH32_wrapper(const void* src, size_t srcSize, void* dst, size_t dstCapacity, void* customPayload)
 {
     (void)dst; (void)dstCapacity; (void)customPayload;
-    return (size_t) XXH32(src, srcSize, 0);
+    return (size_t) crc32c_refl_vpclmulqdq(0, src, srcSize);
 }
 

I also worked around a warning from GCC 15.2.0 elsewhere:

diff --git a/tests/test_alias.c b/tests/test_alias.c
index af1065f..0ed4925 100644
--- a/tests/test_alias.c
+++ b/tests/test_alias.c
@@ -7,7 +7,7 @@
 int main() {
 	// it seems this has to be exactly 24 bytes.
 	union {
-		char x[24];
+		char x[24] __attribute__((nonstring));
 		// force 8-byte alignment without making
 		// aliasable with uint64_t.
 		void *y[3];

The numbers that are output by this single-threaded test seem to be proportional to the execution time. Below is the output from the above patched program for an AMD Ryzen AI 9 HX 370:

 ===  benchmarking 4 hash functions  === 
benchmarking large inputs : from 512 bytes (log9) to 128 MB (log27) 
xxh3   , 74182, 84666, 83672, 89621, 92826, 94608, 95333, 95214, 95421, 95539, 95428, 92181, 89884, 91424, 93322, 74003, 61454, 55054, 54935
XXH32  , 50964, 59345, 66989, 74916, 76056, 76814, 77486, 77244, 77221, 75709, 78064, 77477, 76820, 76725, 77119, 64710, 65586, 67875, 61154
XXH64  , 23776, 25692, 26763, 27317, 27243, 27885, 28019, 27526, 27998, 27915, 26876, 27732, 27498, 27587, 27589, 26237, 26248, 26817, 27648
XXH128 , 47036, 67880, 77207, 84151, 90032, 86994, 90539, 91385, 90459, 93035, 93205, 86592, 85137, 86459, 86074, 65815, 62117, 58866, 52468
Throughput small inputs of fixed size (from 1 to 30 bytes): 
xxh3   , 684966002, 660267932, 686242542, 591918279, 593796881, 653788552, 691280352, 693917070, 796122351, 799086322, 606649579, 607106262, 743145838, 770496269, 773394061, 777579358, 538555002, 532533910, 552009941, 548027883, 543334100, 540782406, 531313570, 531370730, 536021827, 535807626, 533730883, 507836101, 529489272, 527372639
XXH32  , 391192227, 394170674, 392500119, 320799639, 325310066, 300092059, 325021742, 325166174, 319339892, 324440142, 320935576, 319710875, 318605694, 320033480, 318768959, 321140626, 266827038, 269546291, 270451691, 271340836, 271642933, 268264601, 270806888, 263607977, 267720899, 268822868, 266933926, 253647371, 269115020, 269411779
XXH64  , 531855115, 500845328, 445383908, 560522481, 466170811, 401734154, 353106295, 465769876, 387646727, 348885765, 313642848, 373218102, 330846547, 292677619, 263810140, 379560978, 331417038, 284102612, 262724111, 314605881, 275802854, 247115565, 227006968, 313150520, 271040908, 250206062, 235322131, 264888019, 239329223, 218043430
XXH128 , 600416903, 598992972, 597492462, 597645528, 600791325, 598841509, 600595595, 602564178, 543290108, 551507908, 559672652, 567830015, 572610884, 573292836, 461124110, 568812453, 441289653, 443379326, 449714793, 445896308, 443586459, 441661817, 452889973, 451724824, 355037442, 454808260, 453913239, 455387883, 445214930, 434933402
benchmarking random size inputs [1-N] : 
xxh3   , 536254141, 537572868, 538169107, 530058591, 554132343, 567626107, 574962298, 574064510, 575911003, 512575947, 733187750, 730542641, 717504120, 745609269, 744037097, 743641148, 700682822, 680405902, 658648507, 654970094, 643835364, 633237371, 650883337, 651155217, 645069308, 643558465, 641581970, 633572643, 632410158, 635136736
XXH32  , 357112193, 380404113, 393745929, 279481611, 328882531, 327669975, 326555116, 323169043, 304338634, 296146461, 284067481, 285193023, 261837568, 261916119, 271470126, 286578410, 297919080, 310922451, 317438909, 308153526, 310695050, 237882917, 306093451, 292329079, 304391960, 292037006, 301689552, 294104782, 297568491, 288948386
XXH64  , 586983878, 540011057, 505108609, 515782962, 506867672, 482203869, 458474345, 460857782, 454959882, 444087636, 436830029, 426223145, 414820438, 404221782, 389029293, 392782728, 383694624, 373786265, 372277845, 369071752, 358965895, 355644767, 343765355, 345120575, 332244312, 330058777, 327488924, 324763345, 328342498, 316925302
XXH128 , 600494980, 601024972, 600815707, 709364018, 721656467, 717393849, 728587806, 727638953, 714389847, 640170770, 704909059, 691095633, 677935985, 671790219, 664878820, 661493496, 634991187, 617963404, 614205735, 600351968, 591558571, 582670037, 572910083, 570938552, 561659226, 552558359, 551863000, 543461642, 544238249, 540090042
Latency for small inputs of fixed size : 
xxh3   , 232758925, 231240688, 235893537, 218715797, 218702972, 217121209, 215070339, 216124609, 228496964, 227321150, 220489768, 216466240, 225549489, 222822477, 218329570, 228193366, 199722428, 202750981, 207964315, 205000677, 211305233, 204392876, 203269104, 208302695, 207763940, 205726991, 203989265, 192116024, 202467348, 205083400
XXH32  , 141043491, 140301324, 140486628,  98556109,  98245408,  98513733,  98804565,  98542229,  96771917,  96485345,  97056348,  94577797,  99446605,  99849828,  98193147, 105409603,  85836960,  86520583,  85472248,  85250394,  86339994,  85767916,  86837601,  86207302,  86573186,  86054076,  84703667,  85890112,  85643206,  85543224
XXH64  , 192012704, 162931329, 140704020, 186484149, 155708976, 135887600, 119828833, 161083460, 136889778, 118022610, 108682607, 134063193, 119996471, 107955849,  95983286, 134099504, 119074090,  93820245,  96247846, 116644599, 104156252,  95846157,  86348288, 114379316, 105948087,  95571192,  87200480, 101708869,  92938617,  85751257
XXH128 , 236321318, 236073631, 236102127, 208280715, 208310530, 210537461, 207243248, 210688513, 194382849, 193552942, 191795246, 192578612, 193381769, 194508669, 192642081, 194463399, 203166631, 203207549, 194373178, 195655373, 192597790, 200018834, 198876272, 202741660, 200240055, 193788601, 199949428, 196226837, 193538519, 202656164
Latency for small inputs of random size [1-N] : 
xxh3   , 235249462, 235093356, 235020196, 230137534, 226608695, 225257538, 223406822, 222481072, 220464520, 220366994, 220493278, 220447660, 221115749, 221203118, 220894785, 222059533, 220315846, 219357725, 218859180, 219266622, 218436838, 217102632, 217388236, 214020849, 216201591, 215447083, 214364303, 215011443, 214583615, 213633500
XXH32  , 138574687, 138200659, 138254096, 127152154, 118652610, 116125087, 113038971, 111430621, 109049625, 107047720, 106949323, 105718789, 104509137, 104077495, 103943496, 103957514, 102501662, 101947719, 101586218, 100532803,  99238931,  98585253,  97934961,  96996536,  96221852,  95664977,  95513748,  95093612,  95705998,  94256848
XXH64  , 191493620, 173300634, 163516791, 167001254, 166128454, 158375539, 152604393, 153758844, 151630496, 147949428, 144272111, 141345000, 138796754, 136163707, 132040948, 133526774, 131023326, 128485376, 128065667, 127330599, 126536183, 124805157, 121684005, 122473701, 119697993, 117956051, 117532869, 116771538, 117506207, 114484195
XXH128 , 235632251, 236305541, 235991633, 228243862, 223842736, 222420190, 219975757, 218645414, 214840004, 210478170, 210057345, 207610543, 205595771, 204385614, 203421740, 202734423, 201489640, 201351797, 202091043, 201638052, 201245985, 201648995, 200984775, 200011769, 199330525, 199704794, 198743103, 198247503, 199099688, 198201541

For the first output, the CRC-32C-disguised-as-XXH32 is reporting 3 to 6 times the numbers that the genuine XXH32 would report. While this easily beats the unaccelerated (?) XXH64, the 64-bit xxh3 seems to be a clear winner.

That is, it could be worth the effort to implement the 64-bit xxh3() as an alternative for rec_fold() and dtuple_fold() in a separate piece of work. However, we must keep in mind that the above benchmark is not at all covering cases where a hash is being computed piecewise, like it is the case in dtuple_fold() where we invoke my_crc32c() on each index field separately. In fact, it is dtuple_fold() that is being executed most of the time. That the rec_fold() is tail-calling my_crc32c() on a contiguous buffer only benefits AHI modifications, not key lookups.

@dr-m
dr-m marked this pull request as draft September 9, 2025 13:58
@dr-m

dr-m commented Sep 9, 2025

Copy link
Copy Markdown
Contributor Author

5ff7fec is a start of improving this further. It needs to be combined with the previous changes, that is, introducing innodb_adaptive_hash_index_cells. I realize that we cannot easily replace btr_sea::partition::latch. What we can do is to make the hash tables much like lock_sys.rec_hash, that is, use a combination of a shared btr_sea::partition::latch and an individual rw-lock inside the btr_sea::partition::table array. This should allow us to use mostly a shared btr_sea::partition::latch in places where we currently hold an exclusive one.

@dr-m
dr-m force-pushed the MDEV-37482 branch 2 times, most recently from f87c07d to 7430887 Compare September 17, 2025 07:32
@dr-m dr-m changed the title MDEV-37482: Introduce innodb_adaptive_hash_index_cells MDEV-37482: Contention on btr_sea::partition::latch; introduce innodb_adaptive_hash_index_cells Sep 17, 2025
@dr-m
dr-m marked this pull request as ready for review September 17, 2025 07:56
Comment thread storage/innobase/btr/btr0sea.cc Outdated
Comment thread storage/innobase/btr/btr0sea.cc
"Redo log write size to avoid read-on-write; must be a power of two",
nullptr, nullptr, 512, 512, 4096, 1);


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assertion found while testing MDEV-37482


origin/MDEV-37482 7430887b8069b990194b04eff2597fcdceab9470

# 2025-09-19T03:20:49 [2441544] | 2025-09-19  3:20:38 22 [ERROR] mariadbd: Deadlock found when trying to get lock; try restarting transaction
# 2025-09-19T03:20:49 [2441544] | mariadbd: /data/Server/MDEV-37482/storage/innobase/btr/btr0btr.cc:1109: dberr_t dict_index_t::clear(que_thr_t*): Assertion `!any_ahi_pages()' failed.


Stacktrace

# 2025-09-19T03:24:21 [2441544] #0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140302231103040) at ./nptl/pthread_kill.c:44
# 2025-09-19T03:24:21 [2441544] #1  __pthread_kill_internal (signo=6, threadid=140302231103040) at ./nptl/pthread_kill.c:78
# 2025-09-19T03:24:21 [2441544] #2  __GI___pthread_kill (threadid=140302231103040, signo=6) at ./nptl/pthread_kill.c:89
# 2025-09-19T03:24:21 [2441544] #3  0x000055be1e39e4ac in my_write_core (sig=6) at /data/Server/MDEV-37482/mysys/stacktrace.c:424
# 2025-09-19T03:24:21 [2441544] #4  0x000055be1db572a4 in handle_fatal_signal (sig=6) at /data/Server/MDEV-37482/sql/signal_handler.cc:298
# 2025-09-19T03:24:21 [2441544] #5  <signal handler called>
# 2025-09-19T03:24:21 [2441544] #6  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140302231103040) at ./nptl/pthread_kill.c:44
# 2025-09-19T03:24:21 [2441544] #7  __pthread_kill_internal (signo=6, threadid=140302231103040) at ./nptl/pthread_kill.c:78
# 2025-09-19T03:24:21 [2441544] #8  __GI___pthread_kill (threadid=140302231103040, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
# 2025-09-19T03:24:21 [2441544] #9  0x00007f9add550476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
# 2025-09-19T03:24:21 [2441544] #10 0x00007f9add5367f3 in __GI_abort () at ./stdlib/abort.c:79
# 2025-09-19T03:24:21 [2441544] #11 0x00007f9add53671b in __assert_fail_base (fmt=0x7f9add6eb130 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x55be1e8a58c2 "!any_ahi_pages()", file=0x55be1e8a5ce0 "/data/Server/MDEV-37482/storage/innobase/btr/btr0btr.cc", line=1109, function=<optimized out>) at ./assert/assert.c:92
# 2025-09-19T03:24:21 [2441544] #12 0x00007f9add547e96 in __GI___assert_fail (assertion=0x55be1e8a58c2 "!any_ahi_pages()", file=0x55be1e8a5ce0 "/data/Server/MDEV-37482/storage/innobase/btr/btr0btr.cc", line=1109, function=0x55be1e8a7548 "dberr_t dict_index_t::clear(que_thr_t*)") at ./assert/assert.c:101
# 2025-09-19T03:24:21 [2441544] #13 0x000055be1e18ea7e in dict_index_t::clear (this=this@entry=0x7f9a40048e98, thr=thr@entry=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/btr/btr0btr.cc:1109
# 2025-09-19T03:24:21 [2441544] #14 0x000055be1e0ba632 in dict_table_t::clear (this=<optimized out>, thr=thr@entry=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/row/row0log.cc:3798
# 2025-09-19T03:24:21 [2441544] #15 0x000055be1e32566e in row_undo_ins (node=node@entry=0x7f9a400b1178, thr=thr@entry=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/row/row0uins.cc:625
# 2025-09-19T03:24:21 [2441544] #16 0x000055be1e1080fc in row_undo (node=node@entry=0x7f9a400b1178, thr=thr@entry=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/row/row0undo.cc:401
# 2025-09-19T03:24:21 [2441544] #17 0x000055be1e109418 in row_undo_step (thr=thr@entry=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/row/row0undo.cc:442
# 2025-09-19T03:24:21 [2441544] #18 0x000055be1e03473d in que_thr_step (thr=thr@entry=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/que/que0que.cc:551
# 2025-09-19T03:24:21 [2441544] #19 0x000055be1e034a07 in que_run_threads_low (thr=thr@entry=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/que/que0que.cc:609
# 2025-09-19T03:24:21 [2441544] #20 0x000055be1e034ace in que_run_threads (thr=0x7f9a40026d98) at /data/Server/MDEV-37482/storage/innobase/que/que0que.cc:629
# 2025-09-19T03:24:21 [2441544] #21 0x000055be1e158866 in trx_t::rollback_low (this=this@entry=0x7f9ad4441a80, savept=savept@entry=0x0) at /data/Server/MDEV-37482/storage/innobase/trx/trx0roll.cc:121
# 2025-09-19T03:24:21 [2441544] #22 0x000055be1e15b17d in trx_rollback_for_mysql (trx=trx@entry=0x7f9ad4441a80) at /data/Server/MDEV-37482/storage/innobase/trx/trx0roll.cc:218
# 2025-09-19T03:24:21 [2441544] #23 0x000055be1decb49a in innobase_rollback (thd=<optimized out>, rollback_trx=true) at /data/Server/MDEV-37482/storage/innobase/handler/ha_innodb.cc:4736
# 2025-09-19T03:24:21 [2441544] #24 0x000055be1db5d21a in ha_rollback_trans (thd=thd@entry=0x7f9a40000d58, all=all@entry=true) at /data/Server/MDEV-37482/sql/handler.cc:2376
# 2025-09-19T03:24:21 [2441544] #25 0x000055be1d9a2019 in trans_rollback (thd=thd@entry=0x7f9a40000d58) at /data/Server/MDEV-37482/sql/transaction.cc:386
# 2025-09-19T03:24:21 [2441544] #26 0x000055be1d800c51 in mysql_execute_command (thd=thd@entry=0x7f9a40000d58, is_called_from_prepared_stmt=is_called_from_prepared_stmt@entry=false) at /data/Server/MDEV-37482/sql/sql_parse.cc:5549
# 2025-09-19T03:24:21 [2441544] #27 0x000055be1d802957 in mysql_parse (thd=thd@entry=0x7f9a40000d58, rawbuf=<optimized out>, length=<optimized out>, parser_state=parser_state@entry=0x7f9aa8a55330) at /data/Server/MDEV-37482/sql/sql_parse.cc:7905
# 2025-09-19T03:24:21 [2441544] #28 0x000055be1d804eec in dispatch_command (command=command@entry=COM_QUERY, thd=thd@entry=0x7f9a40000d58, packet=packet@entry=0x7f9a4000b119 " ROLLBACK  /* E_R Thread15 QNO 1027 CON_ID 31 */ ", packet_length=packet_length@entry=49, blocking=blocking@entry=true) at /data/Server/MDEV-37482/sql/sql_parse.cc:1903
# 2025-09-19T03:24:21 [2441544] #29 0x000055be1d806ec3 in do_command (thd=thd@entry=0x7f9a40000d58, blocking=blocking@entry=true) at /data/Server/MDEV-37482/sql/sql_parse.cc:1416
# 2025-09-19T03:24:21 [2441544] #30 0x000055be1d98baed in do_handle_one_connection (connect=<optimized out>, connect@entry=0x55be2199e9a8, put_in_cache=put_in_cache@entry=true) at /data/Server/MDEV-37482/sql/sql_connect.cc:1415
# 2025-09-19T03:24:21 [2441544] #31 0x000055be1d98bd2d in handle_one_connection (arg=0x55be2199e9a8) at /data/Server/MDEV-37482/sql/sql_connect.cc:1327
# 2025-09-19T03:24:21 [2441544] #32 0x00007f9add5a2ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
# 2025-09-19T03:24:21 [2441544] #33 0x00007f9add634850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Core dump is present on pluto:-
/data/results/1758228918/TBR-2335

Bug filed:-https://jira.mariadb.org/browse/MDEV-37697

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may share a root cause with the race condition that was fixed in 8aafa20 and b905813.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error is still reproducible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e0a4a1d should address this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @dr-m  ,
i am still hitting this crash :-
This issue was found here:-
 MDEV-37482, origin/MDEV-37482 e0a4a1dc3cfa262266f38871869027c8359e9e4f
 
# 2025-09-25T17:36:32 [468290] | mariadbd: /data/Server/MDEV-37482A/storage/innobase/btr/btr0btr.cc:1109: dberr_t dict_index_t::clear(que_thr_t*): Assertion `!any_ahi_pages()' failed.

Core dump present on pluto:-
/data/results/1758798531/008361

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a456e7d attempts to address this and adds a few more assertions to catch similar trouble earlier.

Comment thread storage/innobase/btr/btr0sea.cc
Comment thread storage/innobase/btr/btr0sea.cc
Comment thread storage/innobase/btr/btr0sea.cc Outdated
Comment thread storage/innobase/sync/srw_lock.cc
Comment thread storage/innobase/include/srw_lock.h
Comment thread storage/innobase/srv/srv0srv.cc
Comment thread storage/innobase/btr/btr0sea.cc

@vlad-lesin vlad-lesin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it was noted in the first commit message, the idea to use a combination of shared btr_sea::partition::latch and page_hash_latch is similar to the one used to reduce contention on lock_sys.latch. So it should indeed decrease the contention on btr_sea::partition::latch. The code looks good to me, at least I haven't found any errors with just reviewing. RQG testing is necessary to be more sure that the code is correct. Approved to push after RQG testing.

Comment thread storage/innobase/btr/btr0sea.cc Outdated
Comment thread storage/innobase/btr/btr0sea.cc
@mariadb-SaahilAlam

Copy link
Copy Markdown
Hi @dr-m  ,
i am still hitting this crash :-
This issue was found here:-
 MDEV-37482, origin/MDEV-37482 e0a4a1dc3cfa262266f38871869027c8359e9e4f
 
# 2025-09-25T17:36:32 [468290] | mariadbd: /data/Server/MDEV-37482A/storage/innobase/btr/btr0btr.cc:1109: dberr_t dict_index_t::clear(que_thr_t*): Assertion `!any_ahi_pages()' failed.

Core dump present on pluto:-
/data/results/1758798531/008361

Comment thread storage/innobase/btr/btr0sea.cc
@dr-m

dr-m commented Sep 29, 2025

Copy link
Copy Markdown
Contributor Author

I squashed this for the final push. The base revision did not change:

git diff b5ee026183cfb3add456653424a6e89f5215d633..3560656fc60cdb60023261a18ff91668066d83ab
diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc
index 20ddc61f35e..7e2ded9f8bb 100644
--- a/storage/innobase/btr/btr0sea.cc
+++ b/storage/innobase/btr/btr0sea.cc
@@ -714,7 +714,8 @@ static uint32_t btr_search_info_update_hash(const btr_cur_t &cursor) noexcept
       Without this special handling, we could end up setting totally
       useless parameters buf_block_t::LEFT_SIDE | 1 << 16 below
       (rebuilding the adaptive hash index on a 1-byte prefix)
-      if all compared secondary index were NULL. */
+      for example when page_cur_search_with_match_bytes() finds matches
+      of LIKE 'a%' in the first index field. */
       left_bytes_fields|= 1;
     else if (left_bytes_fields)
     {
@@ -919,12 +920,11 @@ btr_sea::partition::cleanup_after_erase(ahi_node *erase, page_hash_latch *l)
 #endif
 template<bool ex>
 btr_sea::partition::erase_status
-btr_sea::partition::erase(uint32_t fold, const rec_t *rec) noexcept
+btr_sea::partition::erase(hash_chain &cell, const rec_t *rec) noexcept
 {
   ut_ad(ex ? latch.have_wr() : latch.have_rd());
   ut_ad(btr_search.enabled);
 
-  hash_chain &cell{table.cell_get(fold)};
   page_hash_latch *const hash_lock{ex ? nullptr : &table.lock_get(cell)};
   buf_block_t *block= nullptr;
   if (!ex) hash_lock->lock();
@@ -1810,13 +1810,16 @@ void btr_search_update_hash_on_delete(btr_cur_t *cursor) noexcept
     ut_ad(btr_search.enabled);
     ut_ad(block_index == index);
 
-    btr_sea::partition::erase_status s= part.erase<false>(fold, rec);
+    btr_sea::partition::erase_status s=
+      part.erase<false>(part.table.cell_get(fold), rec);
     if (s == btr_sea::partition::ERASE_RETRY)
     {
       part.latch.wr_lock(SRW_LOCK_CALL);
-      if (UNIV_LIKELY(btr_search.enabled))
+      btr_sea::hash_chain &cell{part.table.cell_get(fold)};
+
+      if (UNIV_LIKELY(cell.first != nullptr))
       {
-        s= part.erase<true>(fold, rec);
+        s= part.erase<true>(cell, rec);
         ut_ad(s != btr_sea::partition::ERASE_RETRY);
       }
       else
diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h
index 24febbe76f5..e697fc18771 100644
--- a/storage/innobase/include/btr0sea.h
+++ b/storage/innobase/include/btr0sea.h
@@ -312,11 +312,11 @@ struct btr_sea
 
     /** Delete a pointer to a record if it exists, and release the latch.
     @tparam ex   true=holding exclusive latch, false=shared latch
-    @param fold  CRC-32C of rec prefix
+    @param cell  hash table cell that may contain the CRC-32C of rec prefix
     @param rec   B-tree leaf page record
     @return status */
     template<bool ex>
-    erase_status erase(uint32_t fold, const rec_t *rec) noexcept;
+    erase_status erase(hash_chain &cell, const rec_t *rec) noexcept;
   };
 
   /** number of hash table entries, to be multiplied by n_parts */

The first hunk is related to ad8509b; I spent some additional time to understand the cmp==0 scenario that @iMineLink had identified.

The remaining hunks should address the crash that @mariadb-SaahilAlam had been filed as MDEV-37716.

xtrabackup_backup_func(): Invoke btr_search_sys_create(), because
innodb_shutdown() assumes that it will have been called.

srv_boot(): Invoke btr_search_sys_create(). This fixes assertion failures
in the test innodb.temporary_table.

btr_sea::create(): Do not invoke enable().

buf_pool_t::create(): Instead of invoking btr_sea::create(),
invoke btr_sea::enable() when needed.
buf_pool_t::clear_hash_index(): Also clear index->search_info.ref_count
btr_search_info_update_hash(): Avoid useless rebuild if cmp==0.
Consider index(col) on a VARCHAR column for which we have the values
col='prefix1' and col='prefix2'. In WHERE col LIKE 'prefix%',
page_cur_search_with_match_bytes() would return 0 as well as
0 matched fields and bytes on each side.

It turns out that page_cur_search_with_match_bytes() can only return
nonzero matched bytes when a binary comparison can be used, such as
with VARBINARY or INT columns.

Co-developed by: Alessandro Vetere
To reduce contention between insert, erase and search, let us mimic
commit b08448d (MDEV-20612).
That is, btr_sea::partition::insert() and btr_sea::partition::erase()
will use a combination of a shared btr_sea::partition::latch and a
tiny page_hash_latch that is pushed down to the btr_sea::hash_table::array.

An exclusive btr_sea::partition::latch will be used in the final part of
btr_search_drop_page_hash_index(), where we must guarantee that all
entries will be removed, as well as in operations that affect an entire
adaptive hash index partition.

btr_sea::hash_chain: Chain of ahi_node hash buckets.

btr_sea::hash_table: A hash table that includes page_hash_latch
interleaved with hash_chain.

page_hash_latch::try_lock(): Attempt to acquire an exclusive latch
without waiting.

btr_search_guess_on_hash(): Acquire also the page_hash_latch in order
to prevent a concurrent modification of the hash bucket chain that our
lookup is traversing.

ha_insert_for_fold(): Remove. Invoke btr_sea::partition::insert() directly.

btr_sea::partition::erase(): Add template<bool ex> for indicating whether
an exclusive or a shared btr_sea::partition::latch is being held.
If the ex=false operation fails to free the memory,
btr_search_update_hash_on_delete() will retry with ex=true.

btr_sea::partition::cleanup_after_erase(): Add an overload for the case
where instead of holding an exclusive latch, we hold a shared latch
along with a page_hash_latch. When not holding an exclusve latch,
we may fail to free the memory, and the caller has to retry with an
exclusive latch.

btr_sea::partition::cleanup_after_erase_start(),
btr_sea::partition::cleanup_after_erase_finish(): Split from
cleanup_after_erase() to reduce the amount of code duplication.

btr_sea::partition::block_mutex: Protect only the linked list of blocks.
The spare block will exclusively be updated via Atomic_relaxed::exchange().

btr_sea::partition::rollback_insert(): Free the spare block in the unlikely
event that the adaptive hash index has been disabled after our invocation
of btr_sea::partition::prepare_insert().

ha_remove_all_nodes_to_page(): Merged to the only caller
btr_search_drop_page_hash_index().

ssux_lock_impl::wr_rd_downgrade(): Downgrade an X latch to S.

srw_lock_debug::wr_rd_downgrade(), srw_lock_impl::wr_rd_downgrade():
Downgrade from exclusive to shared. This operation is unavailable
if _WIN32 or SUX_LOCK_GENERIC is defined.

btr_search_build_page_hash_index(): Downgrade from exclusive to shared
part.latch before starting to insert records into the adaptive hash index.
In multi-batch operation, we preserve the last fr[] value in order to
ensure the correct operation when buf_block_t::LEFT_SIDE is not set.
SET GLOBAL innodb_adaptive_hash_index_cells may be executed
while the server is running. This parameter will be effectively
multiplied by innodb_adaptive_hash_index_parts, because each partition will
contain its own hash table.

Previously, the number of hash table cells in the InnoDB adaptive hash index
depended on the initial innodb_buffer_pool_size and was insufficient
for some workloads, leading to excessively long hash bucket chains.
If innodb_adaptive_hash_index_cells is at its minimum and default value
16381 at startup, it will be derived from the innodb_buffer_pool_size,
for backward compatibility.
@dr-m
dr-m merged commit 3cc9ac0 into 11.8 Sep 30, 2025
15 of 18 checks passed
@dr-m
dr-m deleted the MDEV-37482 branch September 30, 2025 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

5 participants